const { workerData, parentPort } = require('worker_threads')
const fs = require('fs')
const moment = require('moment')
const readline = require('readline')

/**
 * isPm2Hosting 是否为PM2托管
 * logFilePath 日志文件路径
 * start web worker切割日志文件开始位置
 * end web worker切割日志文件结束位置
 * timeRange 需要匹配的时间范围
 */
const { isPm2Hosting, logFilePath, start, end, timeRange } = workerData

const data = {
  packages: {}, // 总下载量
  putLogs: {}, // 更新数据
  periodStats: {
    dayStats: {}, // 最近一天
    lastWeek: {}, // 最近一周
    thisWeek: {}, // 本周
    lastMonth: {}, // 最近一月
    thisMonth: {} // 本月
  }
}

// 正则表达式匹配日志行(兼容verdaccio和PM2,同时匹配不含时间戳原日志,兼容含有ANSI码颜色日志)
const pattern1 =
  /http\s+<--\s+(?:\x1b\[[0-9;]*m)?(200|304)(?:\x1b\[[0-9;]*m)?,\s+user:\s+(?:\x1b\[[0-9;]*m)?(\w+)(?:\x1b\[[0-9;]*m)?\(([\d.]+)\),\s+req:\s+'GET\s+\/([^']+)',\s+bytes:\s+(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?\/(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?/ // verdaccio
const pattern2 =
  /http\s+---\s+(?:\x1b\[[0-9;]*m)?(200|304)(?:\x1b\[[0-9;]*m)?,\s+user:\s+(?:\x1b\[[0-9;]*m)?(\w+)(?:\x1b\[[0-9;]*m)?\(([\d.]+)\),\s+req:\s+'GET\s+\/([^']+)',\s+bytes:\s+(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?\/(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?/ // PM2
const pattern3 =
  /\[(.*?)\] http\s+<--\s+(?:\x1b\[[0-9;]*m)?(200|304)(?:\x1b\[[0-9;]*m)?,\s+user:\s+(?:\x1b\[[0-9;]*m)?(\w+)(?:\x1b\[[0-9;]*m)?\(([\d.]+)\),\s+req:\s+'GET\s+\/([^']+)',\s+bytes:\s+(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?\/(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?/ // verdaccio
const pattern4 =
  /\[(.*?)\]http\s+---\s+(?:\x1b\[[0-9;]*m)?(200|304)(?:\x1b\[[0-9;]*m)?,\s+user:\s+(?:\x1b\[[0-9;]*m)?(\w+)(?:\x1b\[[0-9;]*m)?\(([\d.]+)\),\s+req:\s+'GET\s+\/([^']+)',\s+bytes:\s+(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?\/(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?/ // PM2

const pattern5 =
  /http\s+<--\s+(?:\x1b\[[0-9;]*m)?(201|200|304)(?:\x1b\[[0-9;]*m)?,\s+user:\s+(?:\x1b\[[0-9;]*m)?(\w+)(?:\x1b\[[0-9;]*m)?\(([\d.]+)\),\s+req:\s+'PUT\s+\/([^']+)',\s+bytes:\s+(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?\/(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?/ // verdaccio
const pattern6 =
  /http\s+---\s+(?:\x1b\[[0-9;]*m)?(201|200|304)(?:\x1b\[[0-9;]*m)?,\s+user:\s+(?:\x1b\[[0-9;]*m)?(\w+)(?:\x1b\[[0-9;]*m)?\(([\d.]+)\),\s+req:\s+'PUT\s+\/([^']+)',\s+bytes:\s+(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?\/(?:\x1b\[[0-9;]*m)?(\d+)(?:\x1b\[[0-9;]*m)?/ // PM2

const log_Pattern = isPm2Hosting ? pattern2 : pattern1
const log_TimePattern = isPm2Hosting ? pattern4 : pattern3

const put_Pattern = isPm2Hosting ? pattern6 : pattern5

// 创建逐行读取日志的流
const fileStream = fs.createReadStream(logFilePath, { start, end })
const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity })

// 逐行读取日志
rl.on('line', (line) => {
  if (line.includes('PUT')) {
    const putMatch = line.match(put_Pattern)
    if (putMatch) {
      const [l, _, user, ip, packageName, bytesSent, bytesReceived] = putMatch
      data['putLogs'][packageName] = ip
    }
  }
  // 过滤状态码为200/304的下载日志,兼容含有ANSI码颜色日志
  if (line.includes('200') || line.includes('304') || line.includes('ESC[33m200ESC[39m') || line.includes('ESC[33m304ESC[39m')) {
    // 正则匹配下载总量
    const match = line.match(log_Pattern)
    if (match) {
      /**
       *l 匹配到的行
       *_ 匹配到的行号
       *user 匹配到的用户名
       *ip 匹配到的IP地址
       *path 匹配到的路径
       *bytesSent 匹配到的发送字节数
       *bytesReceived 匹配到的接收字节数
       */
      const [l, _, user, ip, packageName, bytesSent, bytesReceived] = match

      // 统计总数
      data['packages'][packageName] = (data['packages'][packageName] || 0) + 1
    }

    const matchTime = line.match(log_TimePattern)
    if (matchTime) {
      // 匹配到的时间
      const [l, t, _, user, ip, packageName, bytesSent, bytesReceived] = matchTime

      // 获取当前时间并格式化为标准时间格式

      // 解析日志时间
      const logTime = new Date(t).getTime()

      const weekly = moment(logTime).format('YYYY-w')

      if (!data[weekly]) data[weekly] = {}
      data[weekly][packageName] = (data[weekly][packageName] || 0) + 1

      Object.keys(data.periodStats)?.forEach((i) => {
        updatePeriodStats(i, logTime, packageName, timeRange[i][0], timeRange[i][1], weekly)
      })
    }
  }
})

const updatePeriodStats = (period, logTime, packageName, startTime, endTime, weekly) => {
  if (logTime >= startTime && (endTime === undefined || logTime <= endTime)) {
    data.periodStats[period][packageName] = (data.periodStats[period][packageName] || 0) + 1
  }
}

// 读取完成后返回结果
rl.on('close', () => {
  const obj = {
    ...data,
    ...data.periodStats
  }
  delete obj.periodStats
  parentPort.postMessage(obj)
})

// 捕获错误并返回空结果
rl.on('error', (error) => {
  parentPort.postMessage(null)
})
